package com.ficus.road.maintain.task;

import com.ficus.road.maintain.client.FixGpsLocation;
import com.ficus.road.maintain.client.GpsFixClient;
import com.ficus.road.maintain.core.model.GpsHistory;
import com.ficus.road.maintain.dao.GpsHistoryDao;
import com.ficus.road.maintain.util.LngLonUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 修复gps跳点问题
 * <p>
 * 2023/1/10 下午5:01
 */
@Component
@Slf4j
public class GpsFixTaskV2 {

    @Autowired
    GpsFixClient gpsFixClient;
    @Autowired
    GpsHistoryDao gpsHistoryDao;
    @Value("${coverage.jump_threshold}")
    private Double threshold;
    private final int TIME_THRESHOLD = 3600;

    @Scheduled(cron = "${coverage.fix_cron}")
//    @PostConstruct
    public void gpsFix() {
        log.info("跳点修复任务开始...");
        long l = System.currentTimeMillis();
        List<GpsHistory> toBeFixed = gpsHistoryDao.findByQuery(new Query(Criteria.where("is_fixed").is(Boolean.FALSE)));
        Map<String, List<GpsHistory>> carIdGpsMap = toBeFixed.stream().sorted(Comparator.comparing(GpsHistory::getTimestamp))
                .collect(Collectors.groupingBy(GpsHistory::getCarId));
        carIdGpsMap.keySet().forEach(carId -> {
            List<GpsHistory> repetitionList = Lists.newArrayList();
            List<GpsHistory> gpsList = carIdGpsMap.get(carId);
            GpsHistory lastGpsHistory = gpsList.get(gpsList.size() - 1);
            for (int i = 0; i < gpsList.size() - 1; i++) {
                GpsHistory pre = gpsList.get(i);
                GpsHistory post = gpsList.get(i + 1);
                if (post.getTimestamp() - pre.getTimestamp() > TIME_THRESHOLD) {
                    pre.setIsFixed(Boolean.TRUE);
                    gpsHistoryDao.saveOrUpdate(pre);
                    continue;
                }

//                if (l / 1000 - pre.getTimestamp() > TIME_THRESHOLD){
//                    pre.setIsFixed(Boolean.TRUE);
//                    gpsHistoryDao.saveOrUpdate(pre);
//                    continue;
//                }
                //当前点位和最后一个点位相同，直接跳出循环
                if (LngLonUtil.calculateDistance(pre, lastGpsHistory).equals(0d)) {
                    log.info("跳点修复任务中断, 中断位置: timestamp: {}, carId: {}", pre.getTimestamp(), pre.getCarId());
                    break;
                }

                if (LngLonUtil.calculateDistance(pre, post).equals(0d)) {
                    //前后两个点一样,可能是跳点或者车停下了
                    repetitionList.add(pre);
                    continue;
                }

                Double distance = LngLonUtil.calculateDistance(pre, post);
                // 出现跳点
                if (distance > threshold) {
                    repetitionList.add(pre);
                    // 找到应补点
                    List<FixGpsLocation> fixGpsList = gpsFixClient.fix(pre, post);
                    int fixWindow = fixGpsList.size();
                    //理论上需要的点位个数
                    long needLocationNum = (post.getTimestamp() - repetitionList.get(0).getTimestamp()) / 2;
                    if (fixWindow == 0 || fixWindow >= needLocationNum * 50) {
                        if(fixWindow == 0){
                            log.info("fix failed, fixGpsList is empty,pre={},post={}", pre, post);
                        }else if (fixWindow >= needLocationNum * 3){
                            log.info("时间相差{}秒,返回点位个数={},pre={},post={}", post.getTimestamp() - repetitionList.get(0).getTimestamp(),
                                    fixWindow, pre, post);
                        }

                        repetitionList.forEach(gpsHistory -> {
                            gpsHistory.setIsFixed(Boolean.TRUE);
                            gpsHistoryDao.saveOrUpdate(gpsHistory);
                        });
                        continue;
                    }
                    log.info("开始修复跳点, carId: {}, pre_time: {}, post_time: {}, fixGpsSize={}", pre.getCarId(), pre.getTimestamp(), post.getTimestamp(), fixWindow);
                    int size = repetitionList.size();
                    for (int j = 0; j < needLocationNum; j++) {
                        int round;
                        if (needLocationNum >= fixWindow) {
                            round = Math.min(j, fixWindow - 1);
                        } else {
                            round = Math.round((j + 1) * fixWindow / needLocationNum - 1);
                        }

                        GpsHistory gpsHistory;
                        if (j < size){
                            gpsHistory = repetitionList.get(j);
                        }else {
                            gpsHistory = repetitionList.get(size - 1);
                            gpsHistory.setTimestamp(gpsHistory.getTimestamp() + 2L * (j - size + 1));
                        }
                        FixGpsLocation fixGps = fixGpsList.get(round);
                        gpsHistory.setLatitude(fixGps.getLat());
                        gpsHistory.setLongitude(fixGps.getLng());
                        gpsHistory.setLocation(Lists.newArrayList(fixGps.getLng(), fixGps.getLat()));
                        gpsHistory.setIsFixed(Boolean.TRUE);
                        gpsHistory.setAutoFixed(Boolean.TRUE);
                        gpsHistoryDao.saveOrUpdate(gpsHistory);
                    }
                    repetitionList.clear();
                } else {
                    //没有跳点
                    pre.setIsFixed(Boolean.TRUE);
                    gpsHistoryDao.saveOrUpdate(pre);
                    if (CollectionUtils.isNotEmpty(repetitionList)) {
                        repetitionList.forEach(gpsHistory -> {
                            gpsHistory.setIsFixed(Boolean.TRUE);
                            gpsHistoryDao.saveOrUpdate(gpsHistory);
                        });
                    }
                    repetitionList.clear();
                }
            }
        });
        log.info("跳点修复任务耗时: {}s", (System.currentTimeMillis() - l) / 1000);
    }
}
